#!/bin/bash

# Copyright 2017 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT

# Run multiple early-boot entropy collector tests, with different kernel cmdlines.
#
# This script listens on stdin for Zircon cmdlines. For each line passed to stdin, a new early boot
# test is run with that line passed as the kernel cmdline.

# This script requires an output directory, passed as the first (and only) non-option command line
# argument. There are also a number of options, described in the HELP function below.

set -e -u
CDPATH=
ZIRCONDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"

# Print help message and exit
function HELP {
    if [[ $HELPFLAG -lt 2 ]]; then
        echo "Usage: $0 [options] -- <cmdline1> <cmdline2> ..." >&2
        echo >&2
        echo "Repeatedly runs boot tests, using each provided cmdline in turn" >&2
        echo "Mind your shell escapes when calling this script." >&2
    fi
    echo "Mandatory options (if omitted, you will be prompted):" >&2
    echo "    -n <name>   : nodename (only mandatory for non-qemu (netboot) targets)" >&2
    echo "    -o <dir>    : output directory" >&2
    echo "    -s <source> : entropy source to test: hw_rng, jitterentropy" >&2
    echo "    -t <target> : qemu-x86, qemu-arm64, pc, hikey960, odroidc2, or rpi3" >&2
    echo >&2
    echo "Optional options (if omitted, default as specified):" >&2
    echo "    -b <dir>    : build directory [default: autodetected from -t]" >&2
    echo "    -h          : show help then terminate" >&2
    if [[ $HELPFLAG -lt 2 ]]; then
        echo "                : if passed twice, only print options (meant for" >&2
        echo "                : use by scripts that call $0)" >&2
    fi
    echo "    -l <len>    : bytes of data to collect  [default: 1024*1024]" >&2
    echo "    -r          : Use release build [default: false]" >&2
    exit 1
}

# build-related options
ARCH=
BUILDDIR=
BUILD_ARGS=
BUILD_LEN=$((1024*1024))
BUILD_PROJECT=
BUILDDIR_SUFFIX=
HELPFLAG=0
METHOD=
LEN=$((1024*1024))
NODENAME=
OUTDIR=
SOURCE=
TARGET=

while getopts "b:hl:n:o:rs:t:" FLAG; do
    case "$FLAG" in
        b) BUILDDIR="$OPTARG";;
        h) HELPFLAG=$((HELPFLAG + 1));;
        l)
            LEN="$OPTARG"
            if [[ $LEN -gt $BUILD_LEN ]]; then BUILD_LEN="$LEN"; fi
            ;;
        n) NODENAME="$OPTARG";;
        o) OUTDIR="$OPTARG";;
        r)
            BUILDDIR_SUFFIX+="-release"
            BUILD_ARGS+="DEBUG=0 "
            ;;
        s) SOURCE="$OPTARG";;
        t) TARGET="$OPTARG";;
        \?)
            echo "unrecognized option" >&2
            HELP
            ;;
    esac
done

if (( $HELPFLAG )); then
    HELP
fi


LASTOPT=$((OPTIND-1))
if [[ $LASTOPT -lt 1 || ${!LASTOPT} != "--" ]]; then
    echo "didn't see '--' at end of options" >&2
    HELP
else
    shift $((OPTIND-1))
fi

if [[ -z $OUTDIR ]]; then
    echo -n "output directory: " >&2
    read -r OUTDIR
fi
if [[ ! -d $OUTDIR ]]; then
    echo "missing output directory (tried '$OUTDIR')" >&2
    HELP
fi

# prompt for SOURCE if needed
if [[ -z $SOURCE ]]; then
    echo "choose entropy source:" >&2
    select SOURCE in "hw_rng" "jitterentropy"; do
        if [[ -n $SOURCE ]]; then break; fi
    done
fi
if ! [[ $SOURCE = "hw_rng" || $SOURCE = "jitterentropy" ]]; then
    echo "unrecognized source '$SOURCE'" >&2
    HELP
fi
# handle the TARGET variable
if [[ -z $TARGET ]]; then
    echo "choose target:" >&2
    select TARGET in "qemu-x86" "qemu-arm64" "pc" "hikey960" "odroidc2" "rpi3"; do
        if [[ -n $TARGET ]]; then break; fi
    done
fi
case "$TARGET" in
    qemu-x86)
        ARCH="x86"
        BUILD_PROJECT="x86"
        METHOD=qemu
        ;;
    qemu-arm64)
        ARCH="arm64"
        BUILD_PROJECT="arm64"
        METHOD=qemu
        ;;
    pc)
        ARCH="x86"
        BUILD_PROJECT="x86"
        METHOD=netboot
        ;;
    hikey960 | odroidc2 | rpi3)
        ARCH="arm64"
        BUILD_PROJECT="$TARGET"
        METHOD=netboot
        ;;
    *)
        echo "unrecognized target '$TARGET'" >&2
        HELP
        ;;
esac
if [[ -z $BUILDDIR ]]; then
    BUILDDIR="$ZIRCONDIR/build-$BUILD_PROJECT$BUILDDIR_SUFFIX"
fi

# build Zircon
(
    cd "$ZIRCONDIR"
    scripts/entropy-test/make-parallel -l "$BUILD_LEN" $BUILD_ARGS "$BUILD_PROJECT"
) || exit 1

# choose nodename (after build, since we need netls)
if [[ $METHOD = netboot && -z $NODENAME ]]; then
    readarray -t NODENAMES < <($BUILDDIR/tools/netls --nowait --timeout=1000 |
        sed 's/^\s*device\s\+//;s/\s\+([a-fA-F0-9:/]\+)\s*$//')
    echo "choose device to run on:" >&2
    select NODENAME in "${NODENAMES[@]}"; do
        if [[ -n $NODENAME ]]; then break; fi
    done
fi

# NODENAME is magic: if omitted, we don't pass -n to run-boot-test.
# So, make NODENAME split into either 0 or 2 words when passed unquoted.
if [[ -n $NODENAME ]]; then
    NODENAME="$(printf -- "-n %q" "$NODENAME")"
fi

# run the tests
for CMDLINE in "$@"; do
    STATUS="bad"
    for ((tries = 0; tries < 5; tries++)); do
        # $NODENAME is intentionally not quoted. See above.
        if "$ZIRCONDIR"/scripts/entropy-test/run-boot-test -a "$ARCH" -c "$CMDLINE" -l "$LEN" \
            -m "$METHOD" $NODENAME  -o "$BUILDDIR" -s "$SOURCE" "$OUTDIR"; then
            STATUS="good"
            break
        fi
    done
    if [[ $STATUS != "good" ]]; then
        echo "Failed too many times. Aborting." >&2
        echo "Failed cmdline: $CMDLINE" >&2
        exit 1
    fi
done
